home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / gfx / misc / pchglib12.lha / ToPCHG.c < prev   
C/C++ Source or Header  |  1992-11-15  |  18KB  |  446 lines

  1. ;/* Execute me if you want to compile me.
  2. lc -O -v -j73 -cqusf -rr ToPCHG
  3. blink from ToPCHG.o to ToPCHG lib lib:amiga.lib lib:lcr.lib lib:pchgr.lib SC SD ND
  4. quit
  5.  
  6.                                   ToPCHG.c
  7.  
  8.                             by Sebastiano Vigna
  9.  
  10.  
  11. This file is placed in the public domain.
  12. This program works only under 2.04.
  13.  
  14. ToPCHG will convert one or many ILBM files (FORMs, LISTs and CATs) with
  15. line-by-line palette change info given by SHAM and CTBL to the new PCHG
  16. (Palette Change) format. If there's nothing to change the file is rewritten
  17. with no changes (up to some reordering of the chunks, which is irrelevant by
  18. the ILBM specification). The conversion however is only at file format
  19. level. There is no real image processing done. Thus, in order to follow the
  20. limitation suggested in the PCHG specs, ToPCHG will simply cut out exceeding
  21. changes (i.e., changes on odd line in laced pictures or changes after the
  22. MAX_CHANGES_PER_LINEth one, unless the AllChanges switch is specified). The
  23. result is quite ugly; thus, ToPCHG is mainly an easy way of producing
  24. multi-palette pictures for testing of programs etc. It shouldn't be used to
  25. convert images. I hope someone will release soon some image processing
  26. program that will do a real conversion. However, if you convert a SHAM image
  27. with AllChanges on, you will get all the information you had in the SHAM
  28. chunk in the PCHG chunk (because SHAM is not specifying changes on odd
  29. interlaced lines anyway).
  30.  
  31. You should find the complete specification of the PCHG chunk together with
  32. this file. PCHG allows for exact identification of which registers should be
  33. changed, and which not. It's also (usually) smaller than CTBL or SHAM
  34. chunks. It is documented, and has tools that support it. I hope it'll be
  35. soon supported by freeware/shareware/commercial programs.
  36.  
  37. The Files argument lets you enter as many files (with wildcards) as you
  38. like. ToPCHG will read them, put in a PCHG chunk and rewrite them to the To
  39. directory. You can also specify a single file name, and then To will act as
  40. a destination (it can be either a file name or a directory). That is, ToPCHG
  41. works more or less like the rename command.
  42.  
  43. If the switch KillOld is specified, the old-fashioned information will be
  44. deleted. This is good if your paint program supports PCHG or if you just
  45. want to see these pictures with Mostra 1.05, but it's not recommended if you
  46. plan to edit again the file. Instead, just add the PCHG chunk (so Mostra can
  47. display the picture). Beware of the fact that probably editing and saving
  48. the picture will delete the PCHG chunk (if the program you're using doesn't
  49. support it). If the option KillOld is not used, ToPCHG will leave in your
  50. file both the old and the new CMAP. The second CMAP overrides the first
  51. (again by the ILBM specification).
  52.  
  53. ToPCHG will use by default the very dense 12 bit format. If you specify the
  54. switch USE24BIT, the 24bit+alpha channel mode will be used instead. The
  55. chunk can also be compressed using the switch COMP.
  56.  
  57. The switch AllChanges overrides the default behaviour of cutting the color
  58. changes to MAX_COLOR_CHANGES (=7), as suggested in the PCHG specs. This can
  59. be useful for converting SHAM images, but it will produce picture whose look
  60. depends on their position on the screen.
  61.  
  62. I'm sorry for the confusion and the poor documentation of the C code that follows.
  63. It was written in a hurry, and I hadn't so much time to work on it. People building
  64. easier interfaces (AppWindow for instance) to this conversion code are welcome.
  65.  
  66. */
  67.  
  68.  
  69. #include <proto/iffparse.h>
  70. #include <proto/dos.h>
  71. #include <proto/exec.h>
  72. #include <libraries/iffparse.h>
  73. #include <dos/dosasl.h>
  74. #include <exec/memory.h>
  75. #include <string.h>
  76. #include <graphics/view.h>
  77. #include <math.h>
  78. #include <time.h>
  79. #include <iff/pchg.h>
  80. #include <clib/pchglib_protos.h>
  81.  
  82. /*  Bitmap header (BMHD) structure  */
  83. struct BitMapHeader {
  84.     UWORD    w, h;        /*  Width, height in pixels */
  85.     WORD    x, y;        /*  x, y position for this bitmap  */
  86.     UBYTE    nplanes;    /*  # of planes  */
  87.     UBYTE    Masking;
  88.     UBYTE    Compression;
  89.     UBYTE    pad1;
  90.     UWORD    TransparentColor;
  91.     UBYTE    XAspect, YAspect;
  92.     WORD    PageWidth, PageHeight;
  93. };
  94.  
  95.  
  96. char Template[] = "Files/M/A,To/A,KillOld/S,Use24Bit/S,AllChanges/S,Comp/S";
  97. char EmbeddedVersion[] = "\0$VER: ToPCHG 37.4 (14.7.92)";
  98.  
  99. #define OPT_FILES        0
  100. #define OPT_TO            1
  101. #define OPT_KILLOLD    2
  102. #define OPT_24BIT        3
  103. #define OPT_ALLCHANGES 4
  104. #define OPT_COMP        5
  105.  
  106. #define ID_BODY MAKE_ID('B','O','D','Y')
  107. #define ID_CTBL MAKE_ID('C','T','B','L')
  108. #define ID_MPCT MAKE_ID('M','P','C','T')
  109. #define ID_ILBM MAKE_ID('I','L','B','M')
  110. #define ID_CMAP MAKE_ID('C','M','A','P')
  111. #define ID_BMHD MAKE_ID('B','M','H','D')
  112. #define ID_CAMG MAKE_ID('C','A','M','G')
  113. #define ID_SHAM MAKE_ID('S','H','A','M')
  114.  
  115. #define MAX_CHANGES_PER_LINE (7)
  116.  
  117. struct ExecBase *SysBase;
  118. struct DOSBase *DOSBase;
  119. struct Library *IFFParseBase;
  120.  
  121. static struct AnchorPath Anchor;
  122. static struct FileInfoBlock *fib;
  123. static BPTR DirLock;
  124. static LONG argv[6];
  125.  
  126. static void ConvertToPCHG(char *SouceName, BPTR SourceDir, char *DestName, BPTR DestDir, ULONG KillOld, ULONG Use24Bit, ULONG AllChanges, ULONG Comp);
  127. static void PrintError(int Code);
  128.  
  129. int __saveds __asm main(void) {
  130.  
  131.     int i, rc;
  132.     struct ReadArgs *RA;
  133.  
  134.     SysBase = *(void **)4;
  135.     if (!(DOSBase = (void *)OpenLibrary("dos.library", 37))) return(ERROR_INVALID_RESIDENT_LIBRARY);
  136.  
  137.     if (IFFParseBase = OpenLibrary("iffparse.library", 37)) {
  138.  
  139.         if (RA = ReadArgs(Template, argv, NULL)) {
  140.             i = 0;
  141.             while(((char **)argv[OPT_FILES])[i++]);
  142.             Anchor.ap_BreakBits = SIGBREAKF_CTRL_C;
  143.  
  144. /* Here we decide the relationship source/dest. If the destination is a file, source
  145. has to be a single filename (possibly with wildcards, the first matching file will
  146. be used). If destination is a directory, we scan all sources and place the results in it.
  147. If we have many sources and destination is a file, we issue a ``wrong object type'' error. */
  148.  
  149.             if (fib = AllocDosObject(DOS_FIB, NULL)) {
  150.                 if  ((DirLock = Lock((char *)argv[OPT_TO], ACCESS_READ)) && (rc = Examine(DirLock, fib)) && fib->fib_DirEntryType>0) {
  151.                     rc = i = 0;
  152.  
  153.                     while(!rc && ((char **)argv[OPT_FILES])[i]) {
  154.                         rc = MatchFirst(((char **)argv[OPT_FILES])[i++], &Anchor);
  155.                         while(!rc) {
  156.                             if (Anchor.ap_Info.fib_DirEntryType < 0) {
  157.                                 Printf("Converting %s...\n", Anchor.ap_Info.fib_FileName);
  158.                                 ConvertToPCHG(Anchor.ap_Info.fib_FileName, Anchor.ap_Last->an_Lock, Anchor.ap_Info.fib_FileName, DirLock, argv[OPT_KILLOLD], argv[OPT_24BIT], argv[OPT_ALLCHANGES], argv[OPT_COMP]);
  159.                             }
  160.                             rc = MatchNext(&Anchor);
  161.                         }
  162.                         MatchEnd(&Anchor);
  163.  
  164.                         if (rc == ERROR_NO_MORE_ENTRIES) rc = 0;
  165.                     }
  166.                     if (rc) PrintError(rc);
  167.                 }
  168.                 else if (!DirLock && i>2) PrintError(ERROR_OBJECT_NOT_FOUND);
  169.                 else if (DirLock && rc && i>2) PrintError(ERROR_OBJECT_WRONG_TYPE);
  170.                 else if (i == 2) {
  171.                     UnLock(DirLock);
  172.                     DirLock = NULL;
  173.                     if (rc = MatchFirst(((char **)argv[OPT_FILES])[0], &Anchor)) PrintError(rc);
  174.                     else ConvertToPCHG(Anchor.ap_Info.fib_FileName, Anchor.ap_Last->an_Lock, (char *)argv[OPT_TO], ((struct Process *)FindTask(NULL))->pr_CurrentDir, argv[OPT_KILLOLD], argv[OPT_24BIT], argv[OPT_ALLCHANGES], argv[OPT_COMP]);
  175.                     MatchEnd(&Anchor);
  176.                 }
  177.                 else PrintError(IoErr());
  178.                 if (DirLock) UnLock(DirLock);
  179.                 FreeDosObject(DOS_FIB, fib);
  180.             }
  181.             else PrintError(ERROR_NO_FREE_STORE);
  182.             FreeArgs(RA);
  183.         }
  184.         else PrintError(IoErr());
  185.  
  186.         CloseLibrary(IFFParseBase);
  187.     }
  188.     else PutStr("Can't find iffparse.library\n");
  189.     return(0);
  190. }
  191.  
  192.  
  193. static void PrintError(int Code) {
  194.     PrintFault(Code, (char *)BADDR(((struct CommandLineInterface *)BADDR(((struct Process *)FindTask(NULL))->pr_CLI))->cli_CommandName)+1);
  195. }
  196.  
  197. static int PrintIFFError(int Code) {
  198.     switch(Code) {
  199.         case IFFERR_NOSCOPE:    PutStr("No valid scope for property\n");
  200.                                     break;
  201.         case IFFERR_NOMEM:    PutStr("IFFParse memory allocation failed\n");
  202.                                     break;
  203.         case IFFERR_READ:        PutStr("IFFParse read error\n");
  204.                                     break;
  205.         case IFFERR_SEEK:        PutStr("IFFParse seek error\n");
  206.                                     break;
  207.         case IFFERR_MANGLED:    PutStr("Data in file is corrupt\n");
  208.                                     break;
  209.         case IFFERR_SYNTAX:    PutStr("IFF syntax error\n");
  210.                                     break;
  211.         case IFFERR_NOTIFF:    PutStr("Not an IFF file\n");
  212.                                     break;
  213.         default: break;
  214.     }
  215.     return(Code);
  216. }
  217.  
  218.  
  219. static char ID[8];
  220. static ULONG HoldChunk[] = { ID_MPCT, ID_CMAP, ID_CTBL, ID_SHAM };
  221.  
  222.  
  223. /* Here we take two locks (for two dirs) and two filenames. We convert the
  224. source to the destination. */
  225.  
  226. static void ConvertToPCHG(char *SourceName, BPTR SourceDir, char *DestName, BPTR DestDir, ULONG KillOld, ULONG Use24Bit, ULONG AllChanges, ULONG Comp) {
  227.  
  228.     ULONG in = 0, out = 0, DataSize, TreeSize, SourceSize, ChangeCount, ChangedLines, MaxChanges, TotalChanges, MinReg, MaxReg;
  229.     int i,j,t, Skip;
  230.     struct IFFHandle *iffi = NULL, *iffo = NULL;
  231.     BPTR TLock;
  232.     int rc;
  233.     struct ContextNode *cn;
  234.     char *b;
  235.     BOOL IsLace, openi = TRUE, openo = TRUE, RestoreOld, IsSHAM = FALSE, IsCTBL = FALSE;
  236.     UWORD (*CTBL)[16] = NULL, *RGB;
  237.     UBYTE *CMAP = NULL;
  238.     ULONG CTBLSize, *LineMask;
  239.     struct StoredProperty *sp;
  240.     struct SmallLineChanges *slc;
  241.     struct BigLineChanges *blc;
  242.     struct BigPaletteChange *pc;
  243.     char *p, *LC = NULL;
  244.     struct PCHGHeader *ph;
  245.     struct PCHGCompHeader *pch;
  246.  
  247.     if (!(LC = AllocVec(sizeof(struct PCHGHeader)+128+(sizeof(struct SmallLineChanges)+sizeof(struct BigPaletteChange)*16)*300, MEMF_PUBLIC | MEMF_CLEAR))) {
  248.         PrintError(ERROR_NO_FREE_STORE);
  249.         return;
  250.     }
  251.  
  252.     TLock = CurrentDir(SourceDir);
  253.     if (!(in = Open(SourceName, MODE_OLDFILE))) PrintError(IoErr());
  254.     CurrentDir(TLock);
  255.  
  256.     if (iffi = AllocIFF()) iffi->iff_Stream = in;
  257.     else PrintError(ERROR_NO_FREE_STORE);
  258.  
  259.     if (in && iffi) {
  260.         InitIFFasDOS(iffi);
  261.         if (!PrintIFFError(openi = OpenIFF(iffi, IFFF_READ))) {
  262.  
  263. /* These are the chunk we need to gather for our conversion code */
  264.  
  265.             PropChunk(iffi, ID_ILBM, ID_CMAP);
  266.             PropChunk(iffi, ID_ILBM, ID_CTBL);
  267.             PropChunk(iffi, ID_ILBM, ID_MPCT);
  268.             PropChunk(iffi, ID_ILBM, ID_BMHD);
  269.             PropChunk(iffi, ID_ILBM, ID_SHAM);
  270.             PropChunk(iffi, ID_ILBM, ID_CAMG);
  271.  
  272.             TLock = CurrentDir(DestDir);
  273.             if (!(out = Open(DestName, MODE_NEWFILE))) PrintError(IoErr());
  274.             CurrentDir(TLock);
  275.  
  276.             if (iffo = AllocIFF()) iffo->iff_Stream = out;
  277.             else PrintError(ERROR_NO_FREE_STORE);
  278.  
  279.             if (out && iffo) {
  280.                 InitIFFasDOS(iffo);
  281.                 if (!PrintIFFError(openo = OpenIFF(iffo, IFFF_WRITE))) do {
  282.                     PrintIFFError(rc = ParseIFF(iffi, IFFPARSE_STEP));
  283.                     cn = CurrentChunk(iffi);
  284.  
  285. /* If we get an EOC (end of context), there are three cases: we are out of a wrapper
  286. (FORM, CAT, etc.) and then we simply PopChunk(); or we are out of a chunk we
  287. are gathering (and then we write it, unless KillOld is on and the chunk is a
  288. HoldChunk), or it's a chunk we're not gathering (and then we do nothing). This
  289. mechanism is necessary because iffparse complains if we read manually a chunk
  290. we asked to be gathered. */
  291.  
  292.                     if (rc == IFFERR_EOC) {
  293.                             if (cn->cn_Type == ID_ILBM && ((cn->cn_ID == ID_MPCT && !KillOld) || cn->cn_ID == ID_BMHD || cn->cn_ID == ID_CAMG || (cn->cn_ID == ID_CTBL && !KillOld) || (cn->cn_ID == ID_SHAM && !KillOld) || (cn->cn_ID == ID_CMAP && !KillOld))) {
  294.                             PrintIFFError(rc = PushChunk(iffo, cn->cn_Type, cn->cn_ID, cn->cn_Size));
  295.                             sp = FindProp(iffi, cn->cn_Type, cn->cn_ID);
  296.                             PrintIFFError(WriteChunkBytes(iffo, sp->sp_Data, sp->sp_Size));
  297.                             PrintIFFError(rc = PopChunk(iffo));
  298.                         }
  299.                         else if (cn->cn_ID == ID_FORM || cn->cn_ID == ID_CAT || cn->cn_ID == ID_LIST || cn->cn_ID == ID_PROP)
  300.                             PrintIFFError(rc = PopChunk(iffo));
  301.                         else rc = 0;
  302.                     }
  303.                     else if (!rc) {
  304.                         IDtoStr(cn->cn_ID, ID);
  305.                         Printf("Found chunk; ID: %s", ID);
  306.                         IDtoStr(cn->cn_Type, ID);
  307.                         Printf("  Type: %s  Size: %ld\n", ID, cn->cn_Size);
  308.  
  309. /* We are just entering a chunk. If it's a wrapper we just PushChunk(), otherwise
  310. if it's a BODY we do our conversion work. Then if it's a HoldChunk we do nothing,
  311. otherwise we write it. Note that if something is wrong in the conversion, all
  312. chunks which were gathered but not written because KillOld was on are written
  313. just before the BODY (this includes MPCT, CMAP, SHAM and CTBL).
  314.  
  315. The code is very clumsy, but I don't think making it well-readable would
  316. change the meaning of life *that* much. */
  317.  
  318.  
  319.                         if (cn->cn_ID == ID_FORM || cn->cn_ID == ID_CAT || cn->cn_ID == ID_LIST || cn->cn_ID == ID_PROP)
  320.                             PrintIFFError(rc = PushChunk(iffo, cn->cn_Type, cn->cn_ID, IFFSIZE_UNKNOWN));
  321.                         else  {
  322.                             if (cn->cn_Type == ID_ILBM && cn->cn_ID == ID_BODY) {
  323.                                 RestoreOld = TRUE;
  324.                                 if ((IsCTBL = ((sp = FindProp(iffi, ID_ILBM, ID_CTBL)) != NULL)) || (IsSHAM = ((sp = FindProp(iffi, ID_ILBM, ID_SHAM)) != NULL))) {
  325.                                     CTBLSize = sp->sp_Size - (IsSHAM ? 2 : 0);
  326.                                     CTBL = (void *)((char *)sp->sp_Data+(IsSHAM ? 2 : 0));
  327.                                     if (IsCTBL) PutStr("Got a CTBL chunk, going to convert to PCHG...\n");
  328.                                     else PutStr("Got a SHAM chunk, going to convert to PCHG...\n");
  329.                                     IsLace = (sp = FindProp(iffi, ID_ILBM, ID_CAMG)) && (*((ULONG *)sp->sp_Data) & LACE);
  330.                                     if (FindProp(iffi, ID_ILBM, ID_MPCT)) PutStr("Got a MPCT chunk, too, it should be a MacroPaint picture...\n");
  331.                                     if ((sp = FindProp(iffi, ID_ILBM, ID_CMAP)) && sp->sp_Size >= 48) {
  332.                                         RestoreOld = FALSE;
  333.                                         CMAP = (UBYTE *)sp->sp_Data;
  334.                                         for(i=0; i<16; i++) {
  335.                                             CMAP[i*3] = (CTBL[0][i] & 0xF00)>>4;
  336.                                             CMAP[i*3+1] = (CTBL[0][i] & 0xF0);
  337.                                             CMAP[i*3+2] = (CTBL[0][i] & 0xF)<<4;
  338.                                         }
  339.                                         PrintIFFError(PushChunk(iffo, ID_ILBM, ID_CMAP, sp->sp_Size));
  340.                                         PrintIFFError(WriteChunkBytes(iffo, CMAP, sp->sp_Size));
  341.                                         PrintIFFError(PopChunk(iffo));
  342.  
  343.                                         MaxReg = TotalChanges = ChangedLines = MaxChanges = 0;
  344.                                         MinReg = 16;
  345.                                         PrintIFFError(PushChunk(iffo, ID_ILBM, ID_PCHG, IFFSIZE_UNKNOWN));
  346.                                         ph = (void *)LC;
  347.                                         p = (void *)(LineMask = (void *)&ph[1]);
  348.                                         blc = (void *)(slc = (void *)(LineMask+((CTBLSize/(16*sizeof(UWORD)))*(1+(IsSHAM && IsLace))+31)/32));
  349.                                         Skip = 1+(IsLace && IsCTBL);
  350.                                         for(i=Skip; i<CTBLSize/(16*sizeof(UWORD)); i+=Skip) {
  351.                                             ChangeCount = 0;
  352.                                             if (Use24Bit) pc = (void *)&blc[1];
  353.                                             else RGB = (void *)&slc[1];
  354.                                             for(j=0; j<16; j++)
  355.                                                 if ((t = CTBL[i][j]) != CTBL[i-Skip][j]) {
  356.                                                     if ((Use24Bit && (AllChanges || blc->ChangeCount<MAX_CHANGES_PER_LINE)) ||
  357.                                                         (!Use24Bit && (AllChanges || slc->ChangeCount16<MAX_CHANGES_PER_LINE))) {
  358.                                                         if (j>MaxReg) MaxReg = j;
  359.                                                         if (j<MinReg) MinReg = j;
  360.                                                         TotalChanges++;
  361.                                                         if (Use24Bit) {
  362.                                                             blc->ChangeCount++;
  363.                                                             pc->Register = j;
  364.                                                             pc->Red = (t & 0xF00)>>4;
  365.                                                             pc->Green = t & 0xF0;
  366.                                                             pc->Blue = (t & 0xF)<<4;
  367.                                                             pc++;
  368.                                                         }
  369.                                                         else {
  370.                                                             slc->ChangeCount16++;
  371.                                                             *(RGB++) = t | j<<12;
  372.                                                         }
  373.                                                     }
  374.                                                 }
  375.                                             if ((Use24Bit && (t = blc->ChangeCount)) || (t = slc->ChangeCount16)) {
  376.                                                 ChangedLines++;
  377.                                                 if (Use24Bit) blc = (void *)pc;
  378.                                                 else slc = (void *)RGB;
  379.                                                 if (MaxChanges<t) MaxChanges = t;
  380.                                                 LineMask[(i*(1+(IsSHAM && IsLace)))/32] |= 1<<(31-((i*(1+(IsSHAM && IsLace)))%32));
  381.                                             }
  382.                                         }
  383.                                         SourceSize = (char *)(Use24Bit ? blc : slc)-p;
  384.                                         ph->Flags = Use24Bit ? PCHGF_32BIT : PCHGF_12BIT;
  385.                                         ph->LineCount = (CTBLSize/(16*sizeof(UWORD)))*(1+(IsSHAM && IsLace));
  386.                                         ph->StartLine = 0;
  387.                                         ph->TotalChanges = TotalChanges;
  388.                                         ph->MaxChanges = MaxChanges;
  389.                                         ph->ChangedLines = ChangedLines;
  390.                                         ph->MinReg = MinReg;
  391.                                         ph->MaxReg = MaxReg;
  392.                                         if (Comp) {
  393.                                             p = PCHG_CompHuffmann(LineMask, SourceSize, &DataSize, &TreeSize);
  394.                                             ph->Compression = PCHG_COMP_HUFFMANN;
  395.                                             pch = (void *)&ph[1];
  396.                                             pch->CompInfoSize = TreeSize;
  397.                                             pch->OriginalDataSize = SourceSize;
  398.                                             WriteChunkBytes(iffo, LC, sizeof(struct PCHGHeader)+sizeof(struct PCHGCompHeader));
  399.                                             WriteChunkBytes(iffo, p, TreeSize+DataSize);
  400.                                             FreeMem(p, DataSize+TreeSize);
  401.                                         }
  402.                                         else {
  403.                                             ph->Compression = PCHG_COMP_NONE;
  404.                                             WriteChunkBytes(iffo, LC, SourceSize+sizeof(struct PCHGHeader));
  405.                                         }
  406.                                         PrintIFFError(PopChunk(iffo));
  407.                                     }
  408.                                     else PutStr("No CMAP or CMAP bad size\n");
  409.                                 }
  410.                                 else PutStr("No CTBL/SHAM chunk.\n");
  411.                                 if (RestoreOld && KillOld) {
  412.                                     for(i=0; i<sizeof(HoldChunk)/sizeof(ULONG); i++)
  413.                                         if (sp = FindProp(iffi, ID_ILBM, HoldChunk[i])) {
  414.                                             PrintIFFError(rc = PushChunk(iffo, ID_ILBM, HoldChunk[i], sp->sp_Size));
  415.                                             PrintIFFError(WriteChunkBytes(iffo, sp->sp_Data, sp->sp_Size));
  416.                                             PrintIFFError(rc = PopChunk(iffo));
  417.                                         }
  418.                                 }
  419.                             }
  420.  
  421.                             if (!(cn->cn_Type == ID_ILBM && (cn->cn_ID == ID_MPCT || cn->cn_ID == ID_BMHD || cn->cn_ID == ID_CTBL || cn->cn_ID == ID_SHAM || cn->cn_ID == ID_CAMG || cn->cn_ID == ID_CMAP))) {
  422.                                 PrintIFFError(rc = PushChunk(iffo, cn->cn_Type, cn->cn_ID, cn->cn_Size));
  423.                                 if (cn->cn_Size && (b = AllocMem(cn->cn_Size, MEMF_PUBLIC))) {
  424.                                     ReadChunkBytes(iffi, b, cn->cn_Size);
  425.                                     WriteChunkBytes(iffo, b, cn->cn_Size);
  426.                                     FreeMem(b, cn->cn_Size);
  427.                                 }
  428.                                 else PrintError(rc = ERROR_NO_FREE_STORE);
  429.                                 PopChunk(iffo);
  430.                             }
  431.                         }
  432.                     }
  433.                 } while(!rc);
  434.             }
  435.         }
  436.     }
  437.  
  438.     if (!openi) CloseIFF(iffi);
  439.     if (!openo) CloseIFF(iffo);
  440.     if (in) Close(in);
  441.     if (out) Close(out);
  442.     if (iffi) FreeIFF(iffi);
  443.     if (iffo) FreeIFF(iffo);
  444.     FreeVec(LC);
  445. }
  446.